package net.gdface.facelog;

import java.util.List;
import java.util.Map.Entry;

import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;

import gu.sql2java.IFuzzyMatchFilter.MatchErrorHandler;
import gu.sql2java.BaseBean;
import gu.sql2java.IStringMatchFilter;
import gu.sql2java.StringFieldSearcher;
import gu.sql2java.StringMatchType;
import gu.sql2java.TableManager;
import net.gdface.facelog.db.DeviceGroupBean;
import net.gdface.facelog.db.IDeviceGroupManager;
import net.gdface.facelog.db.IPersonGroupManager;
import net.gdface.facelog.db.PersonGroupBean;
import net.gdface.utils.BeanRelativeUtilits;
import net.gdface.utils.ConditionChecks;

/**
 * 模糊查询
 * @author guyadong
 *
 */
class FuzzyMatchManagement implements MatchErrorHandler<String>,ServiceConstant{
	private final DaoManagement dm;
	/**
	 * 设备组模糊匹配对象
	 */
	private final GroupMatcher<DeviceGroupBean, IDeviceGroupManager> deviceGroupMatcher;
	/**
	 * 人员组模糊匹配对象
	 */
	private final GroupMatcher<PersonGroupBean, IPersonGroupManager> personGroupMatcher;
	FuzzyMatchManagement(DaoManagement dm) {
		this.dm = dm;
		this.deviceGroupMatcher = new GroupMatcher<>(IDeviceGroupManager.class, "name","parent");
		this.personGroupMatcher = new GroupMatcher<>(IPersonGroupManager.class, "name","parent");

	}
	
	FuzzyMatchManagement init(){
		logger.info("Device group fuzzy matcher initializing");
		deviceGroupMatcher.init();
		personGroupMatcher.init();
		return this;
	}
	<B extends BaseBean,M extends TableManager<B>>
	List<MatchEntry> fuzzySearchGroup(GroupMatcher<B, M> groupMatcher,String pattern,IStringMatchFilter matchFilter){
		Multimap<String, Integer> mm = groupMatcher
				.search(pattern, matchFilter);
		List<MatchEntry> entrys = Lists.newArrayList();
		for(Entry<String, Integer> entry:mm.entries()){
			entrys.add(new MatchEntry(entry.getValue(), entry.getKey()));
		}
		// 按匹配的名字排序
		return BeanRelativeUtilits.sortByField(entrys, "key");
	}
	/**
	 * 表字段模糊匹配查询<br>
	 * 根据指定的匹配条件({@code pattern})对{@code tablename}指定的表字段模糊匹配<br>
	 * 比如 '1102'匹配'1楼/1单元/102室'
	 * @param tablename 表名,必须为'fl_device_group'或'fl_person_group'
	 * @param pattern 模糊匹配条件,如'102'
	 * @param matchType 模糊匹配的类型,为{@code null}使用默认值{@link StringMatchType#DIGIT_FUZZY_RIGHT_MATCH}
	 * @param maxMatchCount 要求的最多匹配结果,如果返回的匹配结果超过此值则抛出异常,小于等于0时忽略
	 * @return 匹配结果列表,没有找到匹配记录时返回空表
	 * @throws FuzzyMatchCountExceedLimitException 返回的匹配结果超过{@code maxMatchCount}
	 */
	List<MatchEntry> fuzzySearch(String tablename,String pattern,StringMatchType matchType, int maxMatchCount) 
			throws FuzzyMatchCountExceedLimitException{
		List<MatchEntry> matched;
		if(deviceGroupMatcher.getTablename().equals(tablename)){
			matched = fuzzySearchGroup(deviceGroupMatcher,pattern, matchType == null ? null : matchType.createMatchFilter());
		}else if(personGroupMatcher.getTablename().equals(tablename)){
			matched = fuzzySearchGroup(personGroupMatcher,pattern, matchType == null ? null : matchType.createMatchFilter());
		}else{
			throw new IllegalArgumentException("INVALID tablename " + tablename);
		}
		ConditionChecks.checkTrue(maxMatchCount <= 0 || matched.size() <= maxMatchCount, 
				FuzzyMatchCountExceedLimitException.class,
				"too manay match count %s, exceed max limit %s",matched.size(),maxMatchCount);
		return matched;
		
	}
	@Override
	public void onMatchError(Throwable e, String pattern) {
		logger.error("pattern={},{}:{}",pattern,e.getClass().getSimpleName(),e.getMessage());
	}
	private class GroupMatcher <B extends BaseBean,M extends TableManager<B>> extends StringFieldSearcher<B>{

		public GroupMatcher(Class<M> interfaceClass, String... effectColumnNames) {
			super(interfaceClass, effectColumnNames);
			setDefaultMatchFilter(StringMatchType.DIGIT_FUZZY_RIGHT_MATCH);
			setFunKeyGetter(new GroupPathGetter<>(interfaceClass));
			setErrorHandler(FuzzyMatchManagement.this);
		}

	}
	
	private class GroupPathGetter<B extends BaseBean,M extends TableManager<B>> implements Function<B, String>{
		
		private final Class<M> interfaceClass;
		public GroupPathGetter(Class<M> interfaceClass) {
			this.interfaceClass = interfaceClass;
		}

		@Override
		public String apply(B input) {
			return dm.daoGroupPathOf(interfaceClass,input);
		}
		
	}
}
